use libc::free;
use std::assert;
use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::sync::atomic::{AtomicUsize, Ordering};
#[macro_export]
macro_rules! assert_ {
    ($ expr : expr) => {
        NUM_ASSERT.fetch_add(1, Ordering::SeqCst);
        if !$expr {
            panic!(format!(
                "Assertion failed: {} at {}:{}",
                stringify!($expr),
                file!(),
                line!()
            ));
        }
    };
}
#[repr(C)]
struct UnitTestFunction {
    pub function: fn(),
}
#[allow(non_upper_case_globals)]
#[link(name = "assert", kind = "static")]
static NUM_ASSERT: AtomicUsize = AtomicUsize::new(0);
static TESTS: &[fn()] = &[
    test_malloc_free,
    test_realloc,
    test_calloc,
    test_strdup,
    test_limits,
];
#[test]
fn test_malloc_free() {
    let mut block: *mut c_void;
    let mut block2: *mut c_void;
    let mut block3: *mut c_void;
    let mut block4: *mut c_void;
    let mut ptr: *mut u8;
    let mut i;
    assert!(alloc_test_get_allocated() == 0);
    block = alloc(Layout::from_size_align(1024, 1).unwrap()) as *mut c_void;
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 1024);
    ptr = block as *mut u8;
    for i in 0..1024 {
        assert!(*ptr.add(i) != 0);
    }
    dealloc(block as *mut u8, Layout::from_size_align(1024, 1).unwrap());
    assert!(alloc_test_get_allocated() == 0);
    alloc_test_set_limit(3);
    block = alloc(Layout::from_size_align(1024, 1).unwrap()) as *mut c_void;
    assert!(!block.is_null());
    block2 = alloc(Layout::from_size_align(1024, 1).unwrap()) as *mut c_void;
    assert!(!block2.is_null());
    block3 = alloc(Layout::from_size_align(1024, 1).unwrap()) as *mut c_void;
    assert!(!block3.is_null());
    block4 = alloc(Layout::from_size_align(1024, 1).unwrap()) as *mut c_void;
    assert!(block4.is_null());
    dealloc(block as *mut u8, Layout::from_size_align(1024, 1).unwrap());
    dealloc(block2 as *mut u8, Layout::from_size_align(1024, 1).unwrap());
    dealloc(block3 as *mut u8, Layout::from_size_align(1024, 1).unwrap());
    dealloc(block4 as *mut u8, Layout::from_size_align(1024, 1).unwrap());
}
#[test]
fn test_realloc() {
    let mut block: *mut c_void;
    let mut block2: *mut c_void;
    block2 = malloc(1024);
    block = malloc(1024);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 1024 + 1024);
    block = realloc(block, 2048);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 2048 + 1024);
    block = realloc(block, 1500);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 1500 + 1024);
    free(block);
    assert!(alloc_test_get_allocated() == 0 + 1024);
    block = realloc(ptr::null_mut(), 1024);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 1024 + 1024);
    free(block);
    free(block2);
    assert!(alloc_test_get_allocated() == 0);
    block = malloc(512);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 512);
    alloc_test_set_limit(1);
    block = realloc(block, 1024);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 1024);
    assert!(realloc(block, 2048).is_null());
    assert!(alloc_test_get_allocated() == 1024);
    free(block);
    assert!(alloc_test_get_allocated() == 0);
    alloc_test_set_limit(1);
    block = realloc(ptr::null_mut(), 1024);
    assert!(!block.is_null());
    assert!(alloc_test_get_allocated() == 1024);
    assert!(realloc(ptr::null_mut(), 1024).is_null());
    assert!(alloc_test_get_allocated() == 1024);
    free(block);
}
#[test]
fn test_calloc() {
    let mut block: *mut u8;
    let mut i: usize;
    assert_eq!(alloc_test_get_allocated(), 0);
    unsafe {
        block = libc::calloc(16, 64) as *mut u8;
    }
    assert_eq!(alloc_test_get_allocated(), 1024);
    assert!(!block.is_null());
    for i in 0..1024 {
        unsafe {
            assert_eq!(*block.add(i), 0);
        }
    }
    unsafe {
        libc::free(block as *mut c_void);
    }
    assert_eq!(alloc_test_get_allocated(), 0);
    alloc_test_set_limit(1);
    unsafe {
        block = libc::calloc(1024, 1) as *mut u8;
    }
    assert!(!block.is_null());
    assert_eq!(alloc_test_get_allocated(), 1024);
    unsafe {
        assert!(libc::calloc(1024, 1).is_null());
    }
    assert_eq!(alloc_test_get_allocated(), 1024);
    unsafe {
        libc::free(block as *mut c_void);
    }
}
#[test]
fn test_strdup() {
    assert!(alloc_test_get_allocated() == 0);
    let str = strdup("hello world").unwrap();
    assert!(str != "");
    assert!(str == "hello world");
    assert!(alloc_test_get_allocated() == 12);
    unsafe {
        free(str);
    }
    assert!(alloc_test_get_allocated() == 0);
    alloc_test_set_limit(1);
    let str = strdup("hello world").unwrap();
    assert!(str != "");
    assert!(alloc_test_get_allocated() == 12);
    assert!(strdup("hello world").is_none());
    assert!(alloc_test_get_allocated() == 12);
    unsafe {
        free(str);
    }
}
#[test]
fn test_limits() {
    unsafe {
        let mut block;
        block = safe_malloc(2048);
        assert!(!block.is_null());
        safe_free(block, 2048);
        alloc_test_set_limit(1024);
        block = safe_malloc(1024);
        assert!(!block.is_null());
        assert!(safe_malloc(1024).is_null());
        safe_free(block, 1024);
        alloc_test_set_limit(-1);
        block = safe_malloc(1024);
        assert!(!block.is_null());
        safe_free(block, 1024);
    }
}
fn main() {
    run_tests(tests);
    println!("num_assert: {}", NUM_ASSERT.load(Ordering::Relaxed));
}
